//
//  BindKey.swift
//  CooperSwift_Example
//
//  Created by 计海峰 on 2021/5/26.
//  Copyright © 2021 CocoaPods. All rights reserved.
//

import Foundation
import RxSwift
import CooperSwift

// MARK: - 动态绑定key
private struct AssociateKeys {
    static var bindKey: Void?
    static var targetKey: Void?
    static var targetKeys: Void?
    static var serviceHolderKey: Void?
}

// MARK: - 协议
protocol BindKeyProtocol: NSObjectProtocol, AssociatedObjectStore { }


// MARK: - 执行或者发出方法
extension BindKeyProtocol {
    
    // 执行方法
    @discardableResult
    private func run(_ methodStr: String, info: Any? = nil) -> Bool{
        var methodKey = String(methodStr)
        if info != nil && methodKey.last != ":" {
            methodKey += ":"
        }
        if runPerform(self, method: methodKey, info: info) { return true }
        
        if let holder = serviceHolder {
            methodKey += info != nil ? "parent:" : ":"
            return runPerform(holder, method: methodKey, info: info, parent: self)
        }
        return false
    }
    
    func runPerform(_ target: NSObjectProtocol, method: String, info: Any? = nil, parent: Any? = nil) -> Bool {
        debugPrint("\(target) 执行方法：\(method) 信息：\(info ?? "")")
        let sel = Selector(method)
        if target.responds(to: sel)  {
            if info != nil {
                if parent != nil {
                   target.perform(sel, with: info, with: parent)
                }else {
                    target.perform(sel, with: info)
                }
            }else {
                if parent != nil {
                    target.perform(sel, with: parent)
                }else {
                    target.perform(sel)
                }
            }
            return true
        }
        return false
    }
    
    // 发出方法
    func post(_ methodStr: String, info: Any? = nil, targetKey: String? = nil) {
        
        let methodKey = String(methodStr)
        
        // target key 不为空
        if let key = targetKey {
            sendMethod(key, methodStr: methodKey, info: info)
            return
        }
        
        // 默认target key
        if let key = self.targetKey {
            sendMethod(key, methodStr: methodKey, info: info)
        }
        
        if self.targetKeys.count > 0 {
            self.targetKeys.forEach { key in
                sendMethod(key, methodStr: methodKey, info: info)
            }
        }
        
    }
    
    
    // 发出方法
    func postService(_ service: TableViewModelRunServiceEnum, info: Any? = nil, targetKey: String? = nil) {
        let info = info ?? self.bindKey()
        post(service.runMethod(), info: info, targetKey: targetKey)
    }
    
    func sendMethod(_ targetKey: String, methodStr: String, info: Any? = nil) {
        if targetKey.contains("Observer") {
            NotificationCenter.default.post(name: NSNotification.Name.init(targetKey), object: (methodStr, info))
        }else {
            debugPrint("not found add notification:  \(targetKey)")
        }
    }
    
}

// MARK: - key值绑定&初始化
extension BindKeyProtocol {
    
    // 生成key 方法
    func genKeyRandom() -> String {
        return String(describing: self) + "_\(Date().timeString())_\(Int(arc4random()))"
    }

    // MARK: - key
    private var key: String? {
         get {
            return objc_getAssociatedObject(self, &AssociateKeys.bindKey) as? String
        }
        set {
            objc_setAssociatedObject(self, &AssociateKeys.bindKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
        }
    }
    
    @discardableResult
    func bindKey(_ bag: DisposeBag? = nil) -> String {
        if let key = self.key {
            return key
        }
        let key = genKeyRandom() + (bag != nil ? "_Observer" : "")
        self.key = key
        
        guard let disposeBag = bag else { return key }
        
        //MARK: - 需要绑定监听  接手通知
        NotificationCenter.default.rx
            .notification(NSNotification.Name(key))
//            .take(until: deallocated)
            .subscribe(onNext: { [weak self] notification in
            guard let data = notification.object as? (String, Any?) else {
                return
            }
            self?.run(data.0, info: data.1)
        }).disposed(by: disposeBag)
        return key
    }
    
    // MARK: - target key
    var targetKey: String? {
        get {
            return objc_getAssociatedObject(self, &AssociateKeys.targetKey) as? String
        }
        set {
            objc_setAssociatedObject(self, &AssociateKeys.targetKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
        }
    }
    
    var targetKeys: Set<String> {
        get {
            return associatedObject(forKey: &AssociateKeys.targetKeys, default: [], ploicy: .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        set {
            setAssociatedObject(newValue, forKey: &AssociateKeys.targetKeys, ploicy: .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    func addTargetKey(_ targetKey: String, isTarget: Bool = true) -> Self {
        if isTarget {
            self.targetKey = targetKey
        }else {
            self.targetKeys.insert(targetKey)
        }
        return self
    }

}


// MARK: - 转接实现
class ServiceHolder: NSObject { }

// Table Servuce Holder
extension ServiceHolder {
    
    func byKeySearchIndexPath(_ parent: TableViewModelProtocol, key: String) -> IndexPath? {
        for (sectionIdx, section) in parent.sections.enumerated() {
            for (index, model) in section.items.enumerated() {
                if let info = model as? BindKeyProtocol {
                    if info.bindKey() == key {
                        return IndexPath(row: index, section: sectionIdx)
                    }
                }
            }
        }
        return nil
    }
    
    
    @objc func serviceReloadCell(_ key: String, parent: NSObject) {
        guard let parentTarget = parent as? TableViewModelProtocol else { return }
        guard let tv = parentTarget.currentTV() else { return }
        guard let indexPath = byKeySearchIndexPath(parentTarget, key: key) else { return }
        tv.reloadRows(at: [indexPath], with: .none)
    }
    
    
    @objc func serviceReloadSection(_ key: String, parent: NSObject) {
        guard let parentTarget = parent as? TableViewModelProtocol else { return }
        guard let tv = parentTarget.currentTV() else { return }
        guard let indexPath = byKeySearchIndexPath(parentTarget, key: key) else { return }
        tv.reloadSections([indexPath.section], animationStyle: .none)
    }
    
    @objc func serviceReloadTable(_ key: String, parent: NSObject) {
        guard let parentTarget = parent as? TableViewModelProtocol else { return }
        guard let tv = parentTarget.currentTV() else { return }
        tv.reloadData()
    }
    
    @objc func serviceRemoveCell(_ key: String, parent: NSObject) {
        guard let parentTarget = parent as? TableViewModelProtocol else { return }
        guard let indexPath = byKeySearchIndexPath(parentTarget, key: key) else { return }
        guard var section = parentTarget.sections[safe: indexPath.section] else { return }
        section.items.remove(at: indexPath.row)
        parentTarget.sections[indexPath.section] = section
    }
    
}

extension BindKeyProtocol {
    
    private var serviceHolder: ServiceHolder? {
        get {
            return (objc_getAssociatedObject(self, &AssociateKeys.serviceHolderKey) as? ServiceHolder)
        }
        set {
            objc_setAssociatedObject(self, &AssociateKeys.serviceHolderKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    @discardableResult
    func holder() -> ServiceHolder? {
        guard let holder = self.serviceHolder else {
            self.serviceHolder = ServiceHolder()
            return self.serviceHolder!
        }
        return holder
    }
    
}
